FFmpeg & MP4

FFmpeg is a very adaptable tool which can take pretty much any stream and output it as pretty much any other. It can take multiple streams and merge them or it can split a single stream into multiple. This document will focus on how to use FFmpeg to create MP4 files (and associated stuff) for streaming.

Before any flame wars are ignited, I believe that MKV is the superior format. MP4 is limited as to the stream types which are supported but has been around for a long time so can be played by almost anything with a screen. Conversely, MKV is able to hold pretty much anything but probably won't work on your TV, phones or tablets. If you want to archive media with minimal loss, go for MKV. If you want to stream media to pretty much any device, use MP4.

If you need a refresher on Container Formats and Stream Codecs, Google it!

I use MakeMKV to rip DVDs and BluRays, because I'm lazy and it does quite a good job.

The Basics

To convert any* file to an .mp4 file without specifying any options:

ffmpeg -i input.mkv output.mp4

FFmpeg will automatically transcode any* stream so that it best meets the specifications for MP4

FFmpeg - Supported File Formats, Codecs or Features

Optimise for Streaming

With the command above, you would have a standards-compliant MP4 which should work with any device that supports MP4 but, if you were to attempt to access this over a network, you would have to download the entire file before being able to play it.

To be able to play an MP4 immediately, you must move the "MOOV Atom" to the beginning of the file. This can be achieved with the following command:

ffmpeg -i input.mp4 -movflags +faststart output.mp4

Stream Mapping

Many container formats support multiple audio and subtitle streams. Not all MP4 players support a file containing multiple audio streams and some don't even support opening a file with an embedded subtitle track.

It's possible to choose which input video, audio and subtitle stream are included (or not) in the output MP4 using the 'map' option:

ffmpeg -i input.mkv -map 0:v:0 -map 0:a:1 -map 0:s:3 output.mp4

This will 'map' the first video stream (starting from 0), the second audio stream and the fourth subtitle stream from the input container.

It is unusual to have multiple video streams but not impossible. It is common to have audio streams in different languages and to have audio description and commentary tracks. It is also common to have subtitles in different languages and to have descriptive text for the hard of hearing.

Multiple Inputs

It is possible to include video, audio and subtitles from entirely seperate files:

ffmpeg -i input.mkv -i input.flac -i input.sub output.mp4

In this case, ffmpeg would include all of the input streams in the output MP4.

If you would like to carefully choose which input streams were included and in what order, see the Advanced Mapping section below.

Multiple Outputs

So, you want to convert an MKV into an MP4 but extract the Subtitles?

ffmpeg -i input.mkv output.srt -sn output.mp4

The -sn option blocks all subtitle streams.

This feature is more managable with Advanced Mapping and/or filter_complex.

I prefer to do this in two passes, one for each output file, which seems quicker in my experience

FFmpeg Documentation - Creating Multiple Outputs

Strip Input Metadata

To strip ALL metadata; title, tags, encoder information and chapters:

ffmpeg -i input.mkv -map_metadata -1 -fflags +bitexact -flags:v +bitexact -flags:a +bitexact output.mp4

The -map_metadata -1 option removes all global (container) metadata and stream metadata.

The -fflags +bitexact -flags:v +bitexact -flags:a +bitexact options remove encoder information from the container, video streams and audio streams.

To strip metadata while retaining chapters:

ffmpeg -i input.mkv -map_metadata:s:0 -1 -map_metadata:p:0 -1 -fflags +bitexact -flags:v +bitexact -flags:a +bitexact output.mp4

The -map_metadata:s:0 -1 -map_metadata:p:0 -1 options remove metadata from the program (container) and streams.

Note: This method will remove the 'title' metadata tag which is required by some media players. To add a 'title' tag, see Set Output Metadata below.

superuser - Strip metadata from all formats with FFmpeg

Set Output Metadata

It's possible to add metadata 'tags' to the container (global) to the program, chapters or specific streams:

ffmpeg -i input.mkv -metadata:g title="Output" -metadata:s:a language="eng" -metadata:c:0 title="Intro" output.mp4

The -metadata:g title="Output" option sets the global (container) 'title' tag. Global is the default target so :g could be ommitted.

The -metadata:s:a language="eng" option sets the 'language' tag on all audio streams. This could be further refined by specifying a stream index.

The -metadata:c:0 title="Intro" option sets the 'title' tag of the first chapter (index starts at 0).

These options override -map_metadata.

Metadata is automatically copied from the first input container if nothing else is specified.

Kodi Documentation - Video file tagging

Specify Codec

FFmpeg will automatically use the AVC codec for video streams, the AAC codec for audio streams and the mov_text codec for subtitle streams when outputting to MP4.

To output an MP4 with HEVC video, FLAC audio and WebVTT subtitles:

ffmpeg -i input.mkv -vcodec libx265 -acodec flac -scodec webvtt output.mp4

The -vcodec libx265 option outputs HEVC (H.265) using an old switch which is an alias to -c:v.

The -acodec flac option outputs FLAC using an old switch which is an alias to -c:a.

The -scodec webvtt option outputs WebVTT using an old switch which is an alias to -c:s.

Using the new switches, ie. -c:a, allows you to use stream specifiers. The old switch applies to all streams of a type, audio streams in this case.

It should be noted that this MP4 will not play on most media players. While HEVC might be supported on newer devices, FLAC doesn't seem to be supported by anything except software. I have not tested device support for WebVTT.

Specify h264 Profile

Some devices (mostly very old or obsolete) only support the more limited Constrained Baseline or Main profiles.

To specify 'main' profile:

ffmpeg -i input.mkv -c:v libx264 -profile:v high -level:v 4.0 output.mp4

Most modern devices support the more advanced High profile.

Apple's Quicktime only supports *baseline* and *main* profiles and it only supports the 420 colorspace. https://trac.ffmpeg.org/wiki/Encode/VFX

iOS devices do not support 10-bit H.264. https://www.reddit.com/r/VIDEOENGINEERING/comments/sumja3/best_h264_profile_for_web_streaming_on_browsers/

A lot of devices only support H.264 High Level 4.0, 4.1 or 4.2. https://docs.veeplay.com/docs/video-guides/video-codec-types-device-support/

FFmpeg Documentation - H.264 Video Encoding Guide

Wikipedia - Advanced Video Coding

Mix Surround into Stereo

All media playing systems can be expected to be able to reproduce stereo audio. Most will not have a surround sound speaker setup so, the media player will need to mix surround streams into stereo. Many surround to stereo mixing algorithms are imperfect, resulting in quiet speach or reduced bass.

A discussion of the best ways to mix surround to stereo can be found here: https://superuser.com/questions/852400/properly-downmix-5-1-to-stereo-using-ffmpeg

ffmpeg -i input.mkv -c:a libfdk_aac -b:a 128k -filter:a "volume=1.66,pan=stereo|FL=0.5*FC+0.707*FL+0.707*BL+0.5*LFE|FR=0.5*FC+0.707*FR+0.707*BR+0.5*LFE" output.mp4

The options -c:a, -b:a and -filter:a can use a stream index for when there are multiple audio streams.

64kbps per stream channel is recommended, 2 channels in a stereo stream is 128kbps. Citation needed.

The filter can take channel index numbers instead of names but I find it easier to work with named channels.

superuser - Properly downmix 5.1 to stereo using ffmpeg

Scale and Letterbox

In general, it's better not to scale or letterbox. This permanently alters the video. It's far better to allow the media player to do this on-the-fly instead. Certainly, scaling up reduces the quality of the video more than scaling down.

ffmpeg -i input.mkv -filter:v "scale=(iw*sar)*min(1280/(iw*sar)\,720/ih):ih*min(1280/(iw*sar)\,720/ih), pad=1280:720:(1280-iw*min(1280/iw\,720/ih))/2:(720-ih*min(1280/iw\,720/ih))/2" output.mp4

The above command will scale the image to fit 1280x720, without cropping, while maintining the aspect ratio. It will then center and pad the image to completely fill 1280x720.

superuser - Ffmpeg upscale and letterbox a video

Start and Stop

Whether you want to omit a section at the beginning or end of the source video, or if you want to create an output video of specific length:

ffmpeg -i input.mkv -ss 00:10:00 -to 00:20:00 output.mp4

The command above will start the source video at 10 minutes and proceed to copy/transcode 10 minutes to the output file.

Two Pass Encoding

Use Constant Rate Factor to target a certain quality usually resulting in a larger file.

Use Two Pass Encoding to target a certain bitrate and/or a certain file size.

ffmpeg -y -i input -c:v libx264 -b:v 2600k -pass 1 -an -f null /dev/null && \
ffmpeg -i input -c:v libx264 -b:v 2600k -pass 2 -c:a aac -b:a 128k output.mp4

With either of these methods you can use preset, tune and profile.

Ffmpeg Documentation - H.264 Video Encoding Guide

Streaming Bitrates

Recommendations for video bitrate are wide ranging. For 1080p @ 30fps the recommendations start at 4Mbps all the way up to 10Mbps. The list below is what Netflix uses:

1080p HD: 5 Mbps
720p HD: 3 Mbps
480p SD: 1 Mbps

Netflix Help Center - How to get the best video quality

Compatible Video Codecs

Wikipedia - Comparison of video container formats

Compatible Audio Codecs

Wikipedia - Comparison of video container formats

Compatible Subtitle Codecs

Wikipedia - Comparison of video container formats

Pertinent Metadata Tags

Ultimate Compatibility

Technically, MP4 can contain a stream of nearly any type using Private Streams. That said, FFmpeg may not always help you do that.

MP4 has quite a good list of official codecs but, this doesn't mean that ALL mediaplayers will support it.

Common media player devices have a fairly short list of supported codecs. Old media player software often has an even shorter list.

The HTML5 Video element basically only supports AVC/AAC/WebVTT. This is down to web browser compatibility, the HTML Video element itself could support any codec and WhatWG does suggest others.

According to Ffmpeg Documentation - H.264 Video Encoding Guide, "dumb players" only support the YUV planar color space with 4:2:0 chroma subsampling for H.264 video. You may need to use -vf format=yuv420p (or the alias -pix_fmt yuv420p) for your output to work.

According to superuser - What are the differences between H.264 Profiles?, it is best to use the H.264 "Main" profile for web streaming. Most devices that have a screen, and are capable of playing an .mp4 file, support the "Main" profile.

According to Wikipedia - Advanced Video Coding, for 1080p @ 30fps you can use H.264 Main Level 4. Level 4 is the minimum required decoder performance for 1080p @ 30fps, Level 3.1 for 720p @ 30fps and Level 3.0 for 480p @ 30fps.

Progressive is better than Interlaced. According to Wikipedia - Interlaced video, Interlaced use two fields to render a frame whereas only a single field is required with Progressive.

Frame Rate doesn't really matter too much any more; 24fps, 25fps and 30fps are common and all work fine on any device you're likely to use.